﻿using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace SLTimeZoneHelper
{
   [XmlRoot("TZ")]
	public sealed class TimeZone
   {
		[Display(AutoGenerateField = false)]
      [XmlIgnore]
		public Guid ID { get; set; }

		[Display(AutoGenerateField = false)]
      [XmlIgnore]      
		public string SchemaName { get; set; }

      public int Bias { get; set; }

      public string DaylightName { get; set; }

      public string StandardName { get; set; }

      public int TimeZoneCode { get; set; }

      public string UserInterfaceName { get; set; }

      [XmlArray("Rules")]
      [XmlArrayItem("R")]
      public List<TimeZoneRule> TimeZoneRules { get;  set; }

		#region ConvertionLogic
      [XmlIgnore]
		public DateTime Now
		{
			get
			{
				// Just convert the utcnow to local time
				return ConvertToLocal(DateTime.UtcNow);
			}
		}

		/// <summary>
		/// Returns the utc offset for the datetime specified
		/// </summary>
		/// <param name="dt">The local time time</param>
		/// <returns>The utc for the local datetime in the current zone</returns>
		public TimeSpan GetUtcOffset(DateTime dt)
		{
			// Get the Zone Rule for the datetime
			TimeZoneRule zrule = GetRule(dt);
			if (zrule == null)
				return TimeSpan.FromMinutes(-Bias);
			return zrule.GetOffsetFor(dt.AddMinutes(-Bias));	
		}

		/// <summary>
		/// Converts the current datetime to UTC
		/// </summary>
		/// <param name="dt">A datetime to convert.
		/// If it is not of time DateTimeKind.Utc then it is assumed
		/// to be of type DateTimeKind.Local</param>
		/// <returns>A datetime of type DateTimeKind.Utc</returns>
		public DateTime ConvertToUtc(DateTime dt)
		{
			// The the kind is UTC already then use this.
			// Else just assume it is Local even if not specified
			if (dt.Kind == DateTimeKind.Utc)
			{
				return dt;
			}

			// Now just subtract the starhnd offset
			return DateTime.SpecifyKind(dt - GetUtcOffset(dt), DateTimeKind.Utc);
		}

		/// <summary>
		/// Converts the current datetime to local
		/// </summary>
		/// <param name="dt">A datetime to convert.
		/// If it is not of time DateTimeKind.Local then it is assumed
		/// to be of type DateTimeKind.Utc</param>
		/// <returns>A datetime of type DateTimeKind.Local</returns>
		public DateTime ConvertToLocal(DateTime dt)
		{
			// The the kind is Local already then use this.
			// Else just assume it is UTC even if not specified
			if (dt.Kind == DateTimeKind.Local)
			{
				return dt;
			}

			// Now just add the standard offset
			return DateTime.SpecifyKind(dt + GetUtcOffset(dt), DateTimeKind.Local);
		}	

		private TimeZoneRule GetRule(DateTime dt)
		{
			if (TimeZoneRules != null && TimeZoneRules.Any())
			{
				List<TimeZoneRule> rules = TimeZoneRules.ToList();
				rules.Sort((x, y) => 0 - x.EffectiveDateTime.CompareTo(y.EffectiveDateTime)); //from future to past

				return rules.FirstOrDefault(x => x.EffectiveDateTime <= dt);
			}
			return null;
		}
		#endregion
	}
}
